home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung (Tewi)(1994).iso / magazine / drdobbs / 1987 / 09 / holublst.sep < prev    next >
Text File  |  1987-08-13  |  38KB  |  1,033 lines

  1.  
  2.         ; Play four measures of 3/4, followed by four measures of a 3/4
  3.         ; against 4/4 polyrhythm, followed by four measures of 3 against
  4.         ; 4 against 5, followed by four measures of 3 against 4 against 5
  5.         ; against 7. Sound a warning tone one measure before each change.
  6.  
  7.         track 0:  #4 3/4 @120 w,  #4 3/4 @120 w,  #4 3/4 @120 w, #4 3/4                                                 @120 w
  8.         track 1: (#4 3/4)      ,  #4 4/4       ,  #4 4/4       , #4 4/4
  9.         track 2: (#4 3/4)      , (#4 5/4)      ,  #4 5/4       , #4 5/4
  10.         track 3: (#4 3/4)      , (#4 7/4)      , (#4 7/4)      , #4 7/4
  11.  
  12.         ; Now go into Steve Reich mode. Play 20 measures of 4/4 at
  13.         ; metronome 150 and at the same time play 20 measures of 4/4
  14.         ; at metronome 151. The clicks start out on the same
  15.         ; beat and they very gradually go out of phase and then
  16.         ; come back into phase again.
  17.  
  18.         track 0: #20 4/4 @ 150
  19.         track 1: #20 4/4 @ 151
  20.  
  21.  
  22. Example 1: A click program
  23.  
  24.  
  25.  
  26.  
  27.  
  28. Listing 1 -- debug.h
  29.   ____________________________________________________________________________
  30.   1| #define PUBLIC
  31.   2| 
  32.   3| #ifdef  DEBUG
  33.   4| #       define PRIVATE
  34.   5| #       define D(x) x
  35.   6| #else
  36.   7| #       define PRIVATE static
  37.   8| #       define D(x)
  38.   9| #endif
  39.  10| 
  40.  11| #ifdef MSDOS
  41.  12| #       define MS(x) x
  42.  13| #       define UX(x)
  43.  14| #else
  44.  15| #       define MS(x)
  45.  16| #       define UX(x) x
  46.  17| #endif
  47. Listing 2 -- hardware.h
  48.   ____________________________________________________________________________
  49.   1| /* Defines for IBM-PC Hardware */
  50.   2| 
  51.   3| /* Timer defines:
  52.   4|  *
  53.   5|  * TIMR_CLK      The number of system clock cycles in a second
  54.   6|  *               (ie. the input frequency of the counter/timer).
  55.   7|  * TIMR_TICKS(d) The number of ticks needed to get a delay of d
  56.   8|  *               seconds. 'd' can be a fraction: TIMR_TICKS(.5).
  57.   9|  *
  58.  10|  * TIMR_CTRL     Address of control port
  59.  11|  * TIMR_2_DATA   Address of counter 2 data port
  60.  12|  * TIMR_2_LOAD   Control word to load new count into
  61.  13|  *               timer 2 (the speaker--2 bytes, lsb first).
  62.  14|  *               Timer initilized in mode 3 (square wave)
  63.  15|  * TIMR_0_LOAD   Same but for timer 0 (system clock).
  64.  16|  * TIMR_0_DATA
  65.  17|  */
  66.  18| 
  67.  19| #define TIMR_CLK        1193180L
  68.  20| #define TIMR_TICKS(d)   (int)((double)(d) * (TIMR_CLK/65536.0))
  69.  21| 
  70.  22| #define TIMR_CTRL       0x43
  71.  23| 
  72.  24| #define TIMR_0_DATA     0x40
  73.  25| #define TIMR_0_LOAD     0x36
  74.  26| 
  75.  27| #define TIMR_2_DATA     0x42
  76.  28| #define TIMR_2_LOAD     0xb6
  77.  29| 
  78.  30| /* Programmable peripheral interface:
  79.  31|  *
  80.  32|  *      PPI             Base address of interface
  81.  33|  *      PPI_SPKR        Bit mask to enable speaker (bit 0 is
  82.  34|  *                      gate on timer chip, bit 1 actually
  83.  35|  *                      enables the speaker).
  84.  36|  */
  85.  37| 
  86.  38| #define PPI             0x61
  87.  39| #define PPI_SPKR        0x03
  88.  40| 
  89.  41| /*------------------------------------------------------------
  90.  42|  * Make the speaker beep at a particular frequency:
  91.  43|  *
  92.  44|  * SETFRQ(freq);    Sets the frequency, freq can be floating
  93.  45|  *                                                     point
  94.  46|  * SPKR_ON();       Turns the speaker on.
  95.  47|  * SPKR_OFF();      Turns it off again.
  96.  48|  */
  97.  49| 
  98.  50| #define SETFRQ( freq )                             
  99.  51|     if( 1 )                                        
  100.  52|     {                                              
  101.  53|         unsigned int count;                        
  102.  54|                                                    
  103.  55|         count = TIMR_CLK / freq ;                  
  104.  56|                                                    
  105.  57|         outp( TIMR_CTRL  , TIMR_2_LOAD          ); 
  106.  58|         outp( TIMR_2_DATA,  count       & 0xff  ); 
  107.  59|         outp( TIMR_2_DATA, (count >> 8) & 0xff  ); 
  108.  60|     }                                              
  109.  61|     else
  110.  62| 
  111.  63| #define SPKR_ON()  outp( PPI, inp(PPI) |  PPI_SPKR )
  112.  64| #define SPKR_OFF() outp( PPI, inp(PPI) & ~PPI_SPKR )
  113. Listing 3 -- notes.h
  114.   ____________________________________________________________________________
  115.   1| /* These #defines are the frequencies 12 notes of the octave
  116.   2|  * starting with middle C. Multiply by two to go up an octave,
  117.   3|  * divide by two to go down. This is an equal-tempered scale
  118.   4|  * so each note is derrived by multiplying the previous note
  119.   5|  * by the twelfth root of two. Note that there's a little
  120.   6|  * round-off error here but this error isn't audible.
  121.   7|  */
  122.   8| 
  123.   9| #define TWELFTH_ROOT_OF_TWO     1.059463095
  124.  10| 
  125.  11| #define C4         (261.6256)          /*      C  4    */
  126.  12| #define C4_SHARP   (277.1826)          /*      C# 4    */
  127.  13| #define D4         (293.6648)          /*      D  4    */
  128.  14| #define D4_SHARP   (311.1270)          /*      D# 4    */
  129.  15| #define E4         (329.6276)          /*      E  4    */
  130.  16| #define F4         (349.2282)          /*      F  4    */
  131.  17| #define F4_SHARP   (369.9944)          /*      F# 4    */
  132.  18| #define G4         (391.9954)          /*      G  4    */
  133.  19| #define G4_SHARP   (415.3047)          /*      G# 4    */
  134.  20| #define A4         (440.0000)          /*      A  4    */
  135.  21| #define A4_SHARP   (466.1638)          /*      A# 4    */
  136.  22| #define B4         (493.8833)          /*      B  4    */
  137. Listing 4 -- beep.c
  138.   ____________________________________________________________________________
  139.   1| #include <stdio.h>
  140.   2| #include <tools/hardware.h>
  141.   3| 
  142.   4| beep( freq, duration )
  143.   5| double  freq;
  144.   6| double  duration;
  145.   7| {
  146.   8|        /* Beep the bell on the IBM-PC for the indicated time
  147.   9|         * (which may be fractional) at the indicated frequency.
  148.  10|         * Frequencies for various notes in an equal-tempered
  149.  11|         * scale are in <tools/notes.h>.
  150.  12|         */
  151.  13| 
  152.  14|        SETFRQ( freq );
  153.  15| 
  154.  16|        SPKR_ON();
  155.  17| 
  156.  18|        delay( (double)duration );
  157.  19| 
  158.  20|        SPKR_OFF();
  159.  21| }
  160.  22| 
  161.  23| /*------------------------------------------------------------*/
  162.  24| #ifdef MAIN
  163.  25| #include <tools/notes.h>
  164.  26| 
  165.  27| main( argc , argv )
  166.  28| char    **argv;
  167.  29| {
  168.  30|         double atof();
  169.  31| 
  170.  32| #       if 0
  171.  33|             beep(  C * 4 ,      atof(argv[1]) );
  172.  34| #       endif
  173.  35| 
  174.  36|         beep(  C ,      0.5 );
  175.  37|         beep(  D ,      0.5 );
  176.  38|         beep(  E ,      0.5 );
  177.  39|         beep(  F ,      0.5 );
  178.  40|         beep(  G ,      0.5 );
  179.  41|         beep(  A ,      0.5 );
  180.  42|         beep(  B ,      0.5 );
  181.  43|         beep(  C * 2 ,  0.5 );
  182.  44| }
  183.  45| #endif
  184. Listing 5 -- delay.c
  185.   ____________________________________________________________________________
  186.   1| #include <tools/hardware.h>
  187.   2| #include <dos.h>
  188.   3| 
  189.   4| delay( duration )
  190.   5| double  duration;
  191.   6| {
  192.   7|         /*   Delay for the indicated number of seconds (may be
  193.   8|          *   fractional.
  194.   9|          */
  195.  10| 
  196.  11|         unsigned long  start, elapsed ;
  197.  12| 
  198.  13|         unsigned long i, t;
  199.  14| 
  200.  15|         elapsed = TIMR_TICKS( duration );
  201.  16|         
  202.  17|         for( start = ticks(); ticks() - start < elapsed ;)
  203.  18|                 ;
  204.  19| }
  205.  20| 
  206.  21| ticks()
  207.  22| {
  208.  23|     /* Return the number of BIOS clock ticks since midnight.
  209.  24|      * The routine rolls over succesfully at midnight (1573040
  210.  25|      * is the number of clock ticks in a day and AL is zero
  211.  26|      * if the timer has not passed midnight since the last
  212.  27|      * call).
  213.  28|      */
  214.  29| 
  215.  30|     union   REGS    regs;
  216.  31| 
  217.  32|     regs.h.ah = 0;
  218.  33| 
  219.  34|     int86( 0x1a, ®s, ®s );    /* Time-of-day interrupt */
  220.  35| 
  221.  36|     return    ( regs.h.al ? 1573040L : 0 )
  222.  37|             + ( regs.x.cx << 16          )
  223.  38|             +   regs.x.dx
  224.  39|             ;
  225.  40| }
  226.  41| 
  227.  42| #ifdef MAIN
  228.  43| main()
  229.  44| {
  230.  45|         printf("Should be five seconds between beeps\n\007");
  231.  46|         delay( 5.0 );
  232.  47|         printf("\007");
  233.  48| }
  234.  49| #endif
  235. Listing 6 -- speedup.asm, Printed 5/25/1987
  236.   ____________________________________________________________________________
  237.   1|    PAGE   56,132
  238.   2|    TITLE  SPEEDUP.ASM: System-clock-modification routines
  239.   3| ;------------------------------------------------------------
  240.   4| DEBUG   equ 1   ; Set to 1 to make internal symbols public
  241.   5| ;------------------------------------------------------------
  242.   6| 
  243.   7| _TEXT   SEGMENT  BYTE PUBLIC 'CODE'
  244.   8| _TEXT   ENDS
  245.   9| _DATA   SEGMENT  WORD PUBLIC 'DATA'
  246.  10| _DATA   ENDS
  247.  11| CONST   SEGMENT  WORD PUBLIC 'CONST'
  248.  12| CONST   ENDS
  249.  13| _BSS    SEGMENT  WORD PUBLIC 'BSS'
  250.  14| _BSS    ENDS
  251.  15| DGROUP  GROUP   CONST,  _BSS,   _DATA
  252.  16|         ASSUME  CS: _TEXT, DS: DGROUP, SS: DGROUP, ES: DGROUP
  253.  17| 
  254.  18| EXTRN   __chkstk:NEAR
  255.  19| 
  256.  20| ;------------------------------------------------------------
  257.  21| 
  258.  22| TIMR_CTRL   = 43H       ; address of timer control port
  259.  23| TIMR_0_DATA = 40H       ; address of counter 0 data port
  260.  24| TIMR_0_LOAD = 36H       ; control word for timer
  261.  25| 
  262.  26| ;------------------------------------------------------------
  263.  27| 
  264.  28| _TEXT     SEGMENT
  265.  29| 
  266.  30| ;------------------------------------------------------------
  267.  31| ; Misc. variables. Note that I'm putting all these in the
  268.  32| ; code (_TEXT) segment so that I can find them when an
  269.  33| ; interrupt comes along. The PUBLIC statements are just for
  270.  34| ; debugging.
  271.  35| 
  272.  36| old_int    equ  $
  273.  37| old_off    dw   ?       ; Offset of old timer interrupt
  274.  38|                         ;  service routine.
  275.  39| old_seg    dw   ?       ; Segment address of same.
  276.  40| 
  277.  41| service    dw   ?       ; User-supplied interrupt service
  278.  42|                         ; routine (offset).
  279.  43| 
  280.  44| old_ds     dw   ?       ; segments for running program.
  281.  45| tick_reset dw   ?
  282.  46| numticks   dw   ?        ; Initialized to tick_reset, decre-
  283.  47|                         ; mented on each timer interrupt,
  284.  48|                         ; reset to the speedup factor (and the
  285.  49|                         ; old service routine is called) when
  286.  50|                         ; it reaches zero.
  287.  51| 
  288.  52| stack       dw  64 dup (0) ; Local stack for service routine
  289.  53|                            ; 18 bytes are used by real service
  290.  54|                            ; routine, the rest is available
  291.  55|                            ; for the user service routine.
  292.  56| stack_end   dw  ?
  293.  57| old_sp      dw  ?
  294.  58| old_ss      dw  ?
  295.  59| 
  296.  60| IF DEBUG
  297.  61|     PUBLIC  old_int,  old_off,    old_seg,  old_ds
  298.  62|     PUBLIC  service,  tick_reset, numticks, serv
  299.  63| ENDIF
  300.  64| 
  301.  65| ;------------------------------------------------------------
  302.  66| ; cli();  sti();  Disable and enable interrupts.
  303.  67| ;
  304.  68| PUBLIC  _cli, _sti
  305.  69| 
  306.  70| _cli    PROC NEAR
  307.  71|         cli
  308.  72|         ret
  309.  73| _cli    ENDP
  310.  74| 
  311.  75| _sti    PROC NEAR
  312.  76|         sti
  313.  77|         ret
  314.  78| _sti    ENDP
  315.  79| 
  316.  80| ;------------------------------------------------------------
  317.  81| ; speedup( factor, routine )
  318.  82| ; int factor, (*routine)();
  319.  83| ;
  320.  84| ;  Speed up the system clock by the indicated factor.
  321.  85| ;  Call the indicated subroutine on every timer interrupt.
  322.  86| ;  and call the default clock routine as well every "factor"
  323.  87| ;  ticks.
  324.  88| ;
  325.  89| ; Offsets to arguments:
  326.  90| ;       factor  = [bp+4]
  327.  91| ;       routine = [bp+6]
  328.  92| ;
  329.  93| PUBLIC  _speedup
  330.  94| 
  331.  95| _speedup        PROC NEAR
  332.  96|         push    bp
  333.  97|         mov     bp,sp
  334.  98|         xor     ax,ax
  335.  99|         call    __chkstk
  336. 100| 
  337. 101|         mov     ax,[bp+6]            ; service = offset of new
  338. 102|         mov     _TEXT:service,ax     ;  routine.
  339. 103|         mov     _TEXT:old_ds,ds      ; remember current DS too.
  340. 104|         mov     ax,[bp+4]            ; tick_reset = numticks
  341. 105|         mov     _TEXT:tick_reset,ax  ; = ax = factor
  342. 106|         mov     _TEXT:numticks,ax    ;
  343. 107| 
  344. 108|         mov     al,TIMR_0_LOAD       ; Set up timer for load
  345. 109|         out     TIMR_CTRL,al         ;
  346. 110|         mov     ax,[bp+4]            ; if( factor == 1 )
  347. 111|         cmp     ax,01H               ; {
  348. 112|         jne     do_div               ;   use 0 for the ouput count
  349. 113|         mov     ax,0                 ; }
  350. 114|         jmp     load                 ; else
  351. 115| do_div:                              ; {
  352. 116|         mov     ax,00000H            ;     Number of ticks =
  353. 117|         mov     dx,00001H            ;           65536/factor
  354. 118|         mov     bx,[bp+4]            ;     BX = factor.
  355. 119|         div     bx                   ;    AX = number of ticks
  356. 120| load:                                ; }
  357. 121|         out     TIMR_0_DATA,al       ; Send new count to timer
  358. 122|         mov     al,ah                ;
  359. 123|         out     TIMR_0_DATA,al       ;
  360. 124| 
  361. 125|                                      ; Get the old vector
  362. 126|         mov     ah,35H               ;
  363. 127|         mov     al,08H               ;
  364. 128|         int     21H                  ;
  365. 129|         mov     _TEXT:old_off,bx     ;
  366. 130|         mov     _TEXT:old_seg,es     ;
  367. 131| 
  368. 132|                                      ; set up the new vector
  369. 133|         mov     ah,25H               ;
  370. 134|         mov     al,08H               ;
  371. 135|         mov     dx,OFFSET _TEXT: serv ;
  372. 136|         push    ds                    ;
  373. 137|         push    cs                    ;
  374. 138|         pop     ds                    ;
  375. 139|         int     21H                   ;
  376. 140|         pop     ds                    ;
  377. 141| 
  378. 142|         mov     sp,bp
  379. 143|         pop     bp
  380. 144|         ret     
  381. 145| 
  382. 146| _speedup        ENDP
  383. 147| 
  384. 148| ;------------------------------------------------------------
  385. 149| ; Actual interrupt service routine. This routine saves the
  386. 150| ; environment, calles the user-supplied C service routine,
  387. 151| ; and then calls the default service routine if necessary.
  388. 152| ; The service routine runs under its own stack so stack
  389. 153| ; checking should be disabled with either the /Gs command-
  390. 154| ; line switch or the "#pragma check_stack[+|-]" directive.
  391. 155| ;
  392. 156| serv    PROC    NEAR
  393. 157| 
  394. 158|         push    ax                         ; Save AX on old stack
  395. 159| 
  396. 160|         mov     _TEXT:old_sp,sp            ; Set up local
  397. 161|         mov     _TEXT:old_ss,ss            ; stack
  398. 162|         push    cs                         ;
  399. 163|         pop     ss                         ;
  400. 164|         mov     sp,offset _TEXT: stack_end ;
  401. 165| 
  402. 166|         push    bx              ; Set up C environment:
  403. 167|         push    cx              ;       save everything (the
  404. 168|         push    dx              ;       flags are saved as
  405. 169|         push    bp              ;       part of the interrupt
  406. 170|         push    si              ;       processing).
  407. 171|         push    di              ;
  408. 172|         push    ds              ;
  409. 173|         push    es              ;
  410. 174|                                 ;
  411. 175|         mov     ds,_TEXT:old_ds ;       fix the data segment
  412. 176|         mov     es,_TEXT:old_ds ;       and extra segment
  413. 177| 
  414. 178|         cli
  415. 179|         call    word ptr _TEXT:service  ; Call C subroutine
  416. 180|         sti
  417. 181|                                         ;
  418. 182|         pop     es                      ; restore everything
  419. 183|         pop     ds                      ; but AX
  420. 184|         pop     di                      ;
  421. 185|         pop     si                      ;
  422. 186|         pop     bp                      ;
  423. 187|         pop     dx                      ;
  424. 188|         pop     cx                      ;
  425. 189|         pop     bx                      ;
  426. 190| 
  427. 191|         mov     ss,_TEXT:old_ss         ; Restore original
  428. 192|         mov     sp,_TEXT:old_sp         ;       stack.
  429. 193|                                         ;
  430. 194|         dec     _TEXT:numticks          ; if(--numticks > 0)
  431. 195|         jle     do_old_int              ; {
  432. 196|         mov     al,20h                  ;    send EOI
  433. 197|         out     20h,al                  ;
  434. 198|         pop     ax                      ;    restore ax
  435. 199|         iret                            ; }
  436. 200|                                         ; else
  437. 201| do_old_int:                             ; {
  438. 202|         mov     ax,_TEXT:tick_reset     ;    numticks 
  439. 203|         mov     _TEXT:numticks,ax       ;        = tick_reset;
  440. 204|         pop     ax                      ;    restore ax
  441. 205|         jmp     dword ptr _TEXT:old_int ;    jmp to old vector
  442. 206|                                         ; }
  443. 207| serv    ENDP
  444. 208| 
  445. 209| ;------------------------------------------------------------
  446. 210| 
  447. 211| PUBLIC  _slowdown
  448. 212| 
  449. 213| _slowdown       PROC NEAR
  450. 214| 
  451. 215|         push    bp
  452. 216|         mov     bp,sp
  453. 217|         xor     ax,ax
  454. 218|         call    __chkstk
  455. 219| 
  456. 220|         mov     ax,_TEXT:old_off ; See if the interrupts have
  457. 221|         or      ax,ax            ;                 changed.
  458. 222|         jz      no_int           ; No, don't fix them then
  459. 223| 
  460. 224|                                  ; restore old timer interrupt
  461. 225|         push    ds               ;
  462. 226|         mov     ah,25H           ;
  463. 227|         mov     al,08H           ;
  464. 228|         mov     ds,_TEXT:old_seg ;
  465. 229|         mov     dx,_TEXT:old_off ;
  466. 230|         int     21H              ;
  467. 231|         pop     ds               ;
  468. 232| 
  469. 233| no_int:
  470. 234|         mov  al,TIMR_0_LOAD      ; Restore default system
  471. 235|         out  TIMR_CTRL,al        ; clock tick rate
  472. 236|         mov  al,0
  473. 237|         out  TIMR_0_DATA,al
  474. 238|         out  TIMR_0_DATA,al
  475. 239| 
  476. 240|         mov     sp,bp
  477. 241|         pop     bp
  478. 242|         ret     
  479. 243| 
  480. 244| _slowdown       ENDP
  481. 245| 
  482. 246| _TEXT   ENDS
  483. 247| END
  484. Listing 7 -- click.c
  485.   ____________________________________________________________________________
  486.   1| #include <stdio.h>
  487.   2| #include <signal.h>
  488.   3| #include <stdarg.h>
  489.   4| #include <ctype.h>
  490.   5| #include <tools/notes.h>        /* Frequencies of notes */
  491.   6| #include <tools/hardware.h>     /* TIMR_CLK             */
  492.   7| #include <tools/debug.h>        /* D() macro            */
  493.   8| 
  494.   9| /* CLICK.C      A polyrhythmic metronome. Usage is described
  495.  10|  *              in the usage_msg[], below. 
  496.  11|  *
  497.  12|  *              (c) 1987, Allen I. Holub. All rights reserved.
  498.  13|  *------------------------------------------------------------
  499.  14|  */
  500.  15| 
  501.  16| extern double   ceil ( double );
  502.  17| extern double   floor( double );
  503.  18| 
  504.  19| /*------------------------------------------------------------
  505.  20|  * The compiler truncates floating point numbers when they're
  506.  21|  * converted to int. This macro rounds as it converts.
  507.  22|  */
  508.  23| 
  509.  24| #define ROUND(x) ((int)( ((x) > 0.5) ? ceil ((double)(x)) \
  510.  25|                                      : floor((double)(x)) )  )
  511.  26| 
  512.  27| /*------------------------------------------------------------
  513.  28|  * TICKS(x)  converts a metronome count to clock ticks.
  514.  29|  *
  515.  30|  * With a speedup factor 4, a tick happens 72.84 times/second
  516.  31|  * (every .01373 seconds, more or less). A speedup factor of
  517.  32|  * 2 yields half this number: 36.42 times/sec, or an interrupt
  518.  33|  * every .02746 seconds).
  519.  34|  *
  520.  35|  * A metronome 60 is 1 Hz, 120 is 2 Hz, etc.
  521.  36|  *
  522.  37|  * seconds         ==    60 / metronome_count
  523.  38|  * ticks in second == (  60 / metronome_count ) * ONE_TICK
  524.  39|  *                 == ( (60 * ONE_TICK) / metronome_count )
  525.  40|  */
  526.  41| 
  527.  42| #define FACTOR          16                      /* Speedup factor */
  528.  43| #define DEFAULT_TICK    (TIMR_CLK / 65536.0)    /* ticks / second */
  529.  44| #define ONE_TICK        (DEFAULT_TICK * FACTOR)
  530.  45| 
  531.  46| #define TICKS(x)        ROUND( (60.0 * ONE_TICK) / (x) )
  532.  47| 
  533.  48| /*------------------------------------------------------------*/
  534.  49| 
  535.  50| #define min(a,b)        ((a) < (b) ? (a) : (b))
  536.  51| #define max(a,b)        ((a) > (b) ? (a) : (b))
  537.  52| 
  538.  53| #define MAX_MEASURES    1280    /* Max # of measures/track   */
  539.  54| #define NUM_TRACKS      4       /* Number of tracks          */
  540.  55| 
  541.  56| #define WARNING         (NUM_TRACKS + 1)
  542.  57| 
  543.  58| /*------------------------------------------------------------*/
  544.  59| 
  545.  60| typedef unsigned char   uchar;
  546.  61| typedef unsigned int    uint;
  547.  62| 
  548.  63| typedef struct
  549.  64| {
  550.  65|     uchar num_beats;       /* # of beats remaining in measure */
  551.  66|     uchar remainder;       /* Number of ticks to get in synch */
  552.  67|     uint  cur_tick;        /* Current tick for this beat      */
  553.  68|     uint  ticks_per_beat ; /* # of clock ticks between beats  */
  554.  69|     uint  metronome : 14 ; /* metronome count in this measure */
  555.  70|     uint  silent    : 1  ; /* this measure is silent          */
  556.  71|     uint  warning   : 1  ; /* warning tone at downbeat        */
  557.  72| }
  558.  73| MEASURE;
  559.  74| 
  560.  75| typedef MEASURE TRACK [ MAX_MEASURES ];
  561.  76| 
  562.  77| TRACK   Tape     [ NUM_TRACKS ];
  563.  78| MEASURE *Measure [ NUM_TRACKS ];  /* Current measure on */
  564.  79|                                   /* each track of Tape.*/
  565.  80| int     Lineno = 0;               /* Input line number  */
  566.  81| 
  567.  82| int     Ring_bell = 0;  /* 0 if the bell shouldn't ring.
  568.  83|                          * Set to 1 for track 1, 2 for track
  569.  84|                          * 2, etc.
  570.  85|                          */
  571.  86| 
  572.  87| int     Collision = 0 ; /* If two track collide, the track
  573.  88|                          * number of the second one is
  574.  89|                          * put here.
  575.  90|                          */
  576.  91| 
  577.  92| int     Downbeat = 1;   /* Incremented by the interrupt
  578.  93|                          * service routine on every
  579.  94|                          * downbeat from track 0;
  580.  95|                          */
  581.  96| 
  582.  97| int     Done = 0;       /* Set to 1 by interrupt service
  583.  98|                          * routine when it gets to the
  584.  99|                          * end of the tape.
  585. 100|                          */
  586. 101| 
  587. 102| int     Numticks = 0;   /* For debugging, incremented on
  588. 103|                          * every timer interrupt.
  589. 104|                          */
  590. 105| 
  591. 106| /*------------------------------------------------------------*/
  592. 107| 
  593. 108| char    *Usage_msg[] =
  594. 109| {
  595. 110|     "Usage: click [-d] inputfile",
  596. 111|     "",
  597. 112|     " -d (for dull) don't use different notes for different",
  598. 113|     "    tracks",
  599. 114|     "",
  600. 115|     "A polyrhythmic metronome. Four independent \"tracks\"",
  601. 116|     "are supported, with rhythms specified as follows:",
  602. 117|     "",
  603. 118|     "track 0: #4 @120 5/8, #6 @120 6/8, @120 4/4",
  604. 119|     "track 1: #4      6  , #6 @100 6/8",
  605. 120|     "",
  606. 121|     "No line can be longer than 132 characters, but several",
  607. 122|     "track specifiers can be given. The basic notation is:",
  608. 123|     "\"[#N] [@Z] X[/Y],\" interpreted as N measures of X/Y at",
  609. 124|     "metronome Z. If the \"#N\" is missing, 1 is used. If",
  610. 125|     "the \"@Z\" is missing, the measure is stretched to",
  611. 126|     "synch with track one on the down beat. The \"/Y\" is", 
  612. 127|     "optional. So, in the above example, the six beats in",
  613. 128|     "the first four measures of track 2 will synch up with",
  614. 129|     "track one, coming into synch on the downbeat of each",
  615. 130|     "measure. Each track may be up to 1000 measures long.",
  616. 131|     "Lines that don't begin with \"track\" are ignored",
  617. 132|     NULL
  618. 133| };
  619. 134| 
  620. 135| /*------------------------------------------------------------*/
  621. 136| 
  622. 137| char    *skipwhite(p)
  623. 138| char    *p;
  624. 139| {
  625. 140|     /* Skip all characters that aren't part of a command */
  626. 141| 
  627. 142|     while( *p && (isspace(*p) || *p == '\n') )
  628. 143|             p++ ;
  629. 144| 
  630. 145|     return p;
  631. 146| }
  632. 147| 
  633. 148| /*------------------------------------------------------------*/
  634. 149| 
  635. 150| err( fmt )
  636. 151| char    *fmt;
  637. 152| {
  638. 153|         /* Works like printf() but writes to standard error and
  639. 154|          * prints an input line number along with the message.
  640. 155|          */
  641. 156| 
  642. 157|         va_list   args;
  643. 158|         va_start( args, fmt );
  644. 159| 
  645. 160|         fprintf ( stderr, "line %d: ", Lineno );
  646. 161|         vfprintf( stderr, fmt, args );
  647. 162| }
  648. 163| 
  649. 164| /*------------------------------------------------------------*/
  650. 165| 
  651. 166| init()
  652. 167| {
  653. 168|         /* Initialize The Measure array to point
  654. 169|          * at the first measure of each track on the Tape.
  655. 170|          */
  656. 171| 
  657. 172|         register int i;
  658. 173| 
  659. 174|         for( i = NUM_TRACKS; --i >= 0; )
  660. 175|                 Measure[i] = Tape[i] ;
  661. 176| }
  662. 177| 
  663. 178| /*------------------------------------------------------------*/
  664. 179| 
  665. 180| print_tape( this_many )
  666. 181| {
  667. 182|     /* Print out the tape. If this_many is 0, only the
  668. 183|      * initialized measures are printed; otherwise, the
  669. 184|      * indicated number of measures are printed
  670. 185|      */
  671. 186| 
  672. 187|     MEASURE      *p ;
  673. 188|     register int  i , measure_num ;
  674. 189|     int           amt;
  675. 190| 
  676. 191|     for( i = 0; i < NUM_TRACKS ; i++ )
  677. 192|     {
  678. 193|         printf( "Track %d:\n", i );
  679. 194|         measure_num = 0;
  680. 195|         amt         = this_many;
  681. 196| 
  682. 197|         for( p=Tape[i]; p->num_beats>0 || --amt >= 0; p++)
  683. 198|         {
  684. 199|             printf("    measure %2d: ", ++measure_num    );
  685. 200|             printf("[%2d ticks/beat " , p->ticks_per_beat );
  686. 201|             printf("+ %d], "          , p->remainder     );
  687. 202|             printf("cur_tick=%d, "    , p->cur_tick      );
  688. 203|             printf( "%d beats "       , p->num_beats     );
  689. 204| 
  690. 205|             if( p->metronome )
  691. 206|                 printf( "at %-5d ", p->metronome );
  692. 207|             else
  693. 208|                 printf( "in synch " );
  694. 209| 
  695. 210|             printf("%s", p->silent  ? "(mute)" : "" );
  696. 211|             printf("%s", p->warning ? "(warn)" : "" );
  697. 212|             printf("\n");
  698. 213|         }
  699. 214|     }
  700. 215| }
  701. 216| 
  702. 217| /*------------------------------------------------------------*/
  703. 218| 
  704. 219| build_tracks( file_name )
  705. 220| char    *file_name;
  706. 221| {
  707. 222|     FILE        *fp;
  708. 223|     MEASURE     *mp;
  709. 224|     char        buf[133], *line;
  710. 225|     int         i;
  711. 226|     int         track;          /* Track number            */
  712. 227|     int         num_measures;   /* # of measures to repeat */
  713. 228|     int         metronome;      /* metronome count         */
  714. 229|     int         beats;          /* beats per measure       */
  715. 230|     int         ticks;          /* ticks per measure       */
  716. 231|     int         measure;        /* Current measure number  */
  717. 232|     int         silent;         /* measure is silent       */
  718. 233|     int         warning;        /* warning on last repeat  */
  719. 234| 
  720. 235|     if( ! (fp = fopen(file_name,"r")) )
  721. 236|             return 0;
  722. 237| 
  723. 238|     init();
  724. 239| 
  725. 240|     while( line = fgets(buf,133,fp) )
  726. 241|     {
  727. 242|         ++Lineno;
  728. 243| 
  729. 244|         line = skipwhite( line );
  730. 245| 
  731. 246|         if( !(   line[0]=='t' && line[1]=='r'
  732. 247|               && line[2]=='a' && line[3]=='c' && line[4]=='k'
  733. 248|              )
  734. 249|         ) continue;
  735. 250| 
  736. 251|         line += 5;                      /* Get track number     */
  737. 252|         line  = skipwhite (  line );
  738. 253|         track = stoi      ( &line );
  739. 254|         line  = skipwhite (  line );
  740. 255| 
  741. 256|         if( *line == ',' || *line == ':' )
  742. 257|                 line++;
  743. 258| 
  744. 259|         mp      = Measure   [ track ];  /* starting measure #   */
  745. 260|         measure = mp - Tape [ track ];
  746. 261| 
  747. 262|         while( *line )
  748. 263|         {
  749. 264|             num_measures = 1;
  750. 265|             metronome    = 0;
  751. 266|             silent       = 0;
  752. 267|             warning      = 0;
  753. 268| 
  754. 269|             for( line=skipwhite(line); *line; line=skipwhite(line) )
  755. 270|             {
  756. 271|                 if( *line == ';' )              /* comment       */
  757. 272|                 {
  758. 273|                         *line = 0;
  759. 274|                         break;
  760. 275|                 }
  761. 276|                 else if( *line == ',' || *line == ':' )
  762. 277|                 {
  763. 278|                         ++line;                 /* end of measure */
  764. 279|                         break;
  765. 280|                 }
  766. 281|                 if( *line == '#' )              /* # of measures  */
  767. 282|                 {
  768. 283|                     line         = skipwhite ( ++line );
  769. 284|                     num_measures = stoi      (  &line );
  770. 285|                 }
  771. 286|                 else if( *line == '@' )         /* metronome count */
  772. 287|                 {
  773. 288|                     line      = skipwhite ( ++line );
  774. 289|                     metronome = stoi      (  &line );
  775. 290|                 }
  776. 291|                 else if( *line == 'w' || *line == 'W' )
  777. 292|                 {
  778. 293|                     while( isalpha(*line ) )
  779. 294|                         ++line;
  780. 295| 
  781. 296|                     warning = 1;
  782. 297|                 }
  783. 298|                 else if( isdigit( *line ) )     /* Time signature */
  784. 299|                 {
  785. 300|                     if( (beats = stoi(&line)) == 0 )
  786. 301|                     {
  787. 302|                         err( "Illegal time signature\n" );
  788. 303|                         exit(1);
  789. 304|                     }
  790. 305| 
  791. 306|                     if( *line == '/' )          /* Throw away bottom  */
  792. 307|                         ++line;                 /* of time signature. */
  793. 308| 
  794. 309|                     while( isdigit( *line ) )
  795. 310|                         line++;
  796. 311|                 }
  797. 312|                 else if( *line == '(' || *line == ')' )
  798. 313|                 {
  799. 314|                     ++ line   ;
  800. 315|                     silent = 1;
  801. 316|                 }
  802. 317|                 else
  803. 318|                     err("<%c> is illegal in measure spec.\n", *line );
  804. 319|             }
  805. 320| 
  806. 321|             for(; --num_measures >= 0; mp++, measure++ )
  807. 322|             {
  808. 323|                 if( metronome )
  809. 324|                 {
  810. 325|                     mp->metronome = metronome;
  811. 326|                     ticks         = TICKS(metronome) * beats ;
  812. 327|                 }
  813. 328|                 else
  814. 329|                 {
  815. 330|                     mp->metronome = 0;
  816. 331|                     ticks = Tape[0][measure].ticks_per_beat
  817. 332|                           * Tape[0][measure].num_beats
  818. 333|                           ;
  819. 334|                 }
  820. 335| 
  821. 336|                 if( num_measures == 0 ) /* last in series */
  822. 337|                         mp->warning = warning;
  823. 338| 
  824. 339|                 mp->silent         = silent ;
  825. 340|                 mp->num_beats      = beats  ;
  826. 341|                 mp->cur_tick       = ticks / beats;
  827. 342|                 mp->ticks_per_beat = ticks / beats;
  828. 343|                 mp->remainder      = ticks % beats;
  829. 344| 
  830. 345|                 D( printf("loading track %d, ", track        );)
  831. 346|                 D( printf("measure %d: "      , measure      );)
  832. 347|                 D( printf("%d beats/measure " , beats        );)
  833. 348|                 D( printf("at metronome %d\n" , mp->metronome);)
  834. 349|             }
  835. 350| 
  836. 351|             Measure[ track ] = mp ;
  837. 352|         }
  838. 353|     }
  839. 354| 
  840. 355|     init();
  841. 356|     D( print_tape( 0 ); )
  842. 357|     return 1;
  843. 358| }
  844. 359| 
  845. 360| /*------------------------------------------------------------*/
  846. 361| 
  847. 362| #pragma check_stack-    /* Turn off stack probes. This pragma */
  848. 363|                         /* is Microsoft-compiler dependent.   */
  849. 364| 
  850. 365| timer_intr()
  851. 366| {
  852. 367|     /* Interrupt service routine for timer interrupt */
  853. 368| 
  854. 369|     MEASURE  **mp, *p ;
  855. 370|     int         i,  did_nothing ;
  856. 371| 
  857. 372|     Done = 1;
  858. 373|     ++ Numticks;
  859. 374| 
  860. 375|     for( i = 0, mp = Measure; ++i <= NUM_TRACKS ; mp++ )
  861. 376|     {
  862. 377|         if( (p = *mp)->num_beats > 0 )
  863. 378|         {
  864. 379|             if( p->cur_tick==p->ticks_per_beat )
  865. 380|             {
  866. 381|                 /*  Ring bell on first tick of measure
  867. 382|                  *  unless this is a silent measure.
  868. 383|                  *  Warnings take precedence over
  869. 384|                  *  everything.
  870. 385|                  */
  871. 386| 
  872. 387|                 if( p->warning )
  873. 388|                 {
  874. 389|                         Ring_bell = WARNING ;
  875. 390|                         p->warning = 0;
  876. 391|                 }
  877. 392| 
  878. 393|                 else if( !p->silent )
  879. 394|                 {
  880. 395|                     if( !Ring_bell )
  881. 396|                         Ring_bell = i;
  882. 397|                     else
  883. 398|                         Collision = i;
  884. 399|                 }
  885. 400|             }
  886. 401| 
  887. 402|             if( -- p->cur_tick <= 0 )
  888. 403|             {
  889. 404|                 if( -- p->num_beats <= 0 )
  890. 405|                 {
  891. 406|                     if( i == 1 )
  892. 407|                         ++Downbeat;
  893. 408| 
  894. 409|                     ++( *mp );   /* go to next measure */
  895. 410|                 }
  896. 411|                 else
  897. 412|                 {
  898. 413|                     p->cur_tick = p->ticks_per_beat;
  899. 414| 
  900. 415|                     if( p->remainder > 0 )
  901. 416|                     {
  902. 417|                         -- p->remainder ;
  903. 418|                         ++ p->cur_tick  ;
  904. 419|                     }
  905. 420|                 }
  906. 421|             }
  907. 422|             Done = 0;
  908. 423|         }
  909. 424|     }
  910. 425| }
  911. 426| 
  912. 427| #pragma check_stack+
  913. 428| 
  914. 429| /*------------------------------------------------------------*/
  915. 430| 
  916. 431| on_break()
  917. 432| {
  918. 433|         /* Routine for signal(), tries to put the clock rate
  919. 434|          * back to normal on a Ctrl-Break. Note that this
  920. 435|          * routine can fail if Ctrl-Break is hit several times
  921. 436|          * in quick succession.
  922. 437|          */
  923. 438| 
  924. 439|         signal( SIGINT, SIG_IGN );
  925. 440|         slowdown();
  926. 441|         exit(0);
  927. 442| }
  928. 443| 
  929. 444| /*------------------------------------------------------------*/
  930. 445| 
  931. 446| print_stats()
  932. 447| {
  933. 448| }
  934. 449| 
  935. 450| /*------------------------------------------------------------*/
  936. 451| 
  937. 452| usage()
  938. 453| {
  939. 454|         char    **p;
  940. 455|         for( p = Usage_msg; *p; fprintf(stderr,"%s\n", *p++) )
  941. 456|                 ;
  942. 457|         exit(1);
  943. 458| }
  944. 459| 
  945. 460| /*------------------------------------------------------------*/
  946. 461| 
  947. 462| main( argc, argv )
  948. 463| char    **argv;
  949. 464| {
  950. 465|         static int measure = 0; /* current measure in track 0 */
  951. 466|         static int dull    = 0; /* Dull output                */
  952. 467|         static int stats   = 0; /* Statistics only            */
  953. 468|         static int i;
  954. 469| 
  955. 470|         if( argc == 3 )
  956. 471|         {
  957. 472|             --argc;
  958. 473| 
  959. 474|             if( **(++argv) != '-' )
  960. 475|                 usage();
  961. 476| 
  962. 477|             switch( argv[0][1] )
  963. 478|             {
  964. 479|             case 'd': dull  = 1;        break;
  965. 480|             default:                    usage();
  966. 481|             }
  967. 482|         }
  968. 483|         else if( argc != 2 || *argv[1] == '-' )
  969. 484|                 usage();
  970. 485| 
  971. 486|         if( !build_tracks( argv[1] ) )
  972. 487|                 exit( 2 );
  973. 488| 
  974. 489|         if( stats )
  975. 490|         {
  976. 491|                 print_stats();
  977. 492|                 exit(0);
  978. 493|         }
  979. 494| 
  980. 495|         signal( SIGINT, on_break );
  981. 496| 
  982. 497|         for( speedup(FACTOR, timer_intr); !Done; )
  983. 498|         {
  984. 499|             cli();              /* Interrupts off     */
  985. 500| 
  986. 501|             if( Ring_bell == WARNING )
  987. 502|             {
  988. 503|                 Collision  = 0;
  989. 504|                 Ring_bell  = 0;
  990. 505|                 sti();
  991. 506|                 beep ( C4*8,  0.125 );
  992. 507|                 delay(        0.1   );
  993. 508|                 beep ( C4*8,  0.125 );
  994. 509|             }
  995. 510|             else if( Collision )
  996. 511|             {
  997. 512|                 /* the higher track wins */
  998. 513| 
  999. 514|                 i = max( Collision, Ring_bell );
  1000. 515|                 Collision = 0;
  1001. 516|                 Ring_bell = 0;
  1002. 517|                 sti();
  1003. 518|                 beep( dull ? C4*2 : C4 * i, 0.125 );
  1004. 519|             }
  1005. 520|             else if( Ring_bell )
  1006. 521|             {
  1007. 522|                 /* You must set Ring_bell to
  1008. 523|                  * 0 before calling beep so that
  1009. 524|                  * a note won't collide with itself.
  1010. 525|                  */
  1011. 526| 
  1012. 527|                 i = Ring_bell;
  1013. 528|                 Ring_bell = 0;
  1014. 529|                 sti();
  1015. 530|                 beep( dull ? C4*2 : C4 * i, 0.125 );
  1016. 531|             }
  1017. 532|             else
  1018. 533|             {
  1019. 534|                     sti();
  1020. 535|             }
  1021. 536| 
  1022. 537| 
  1023. 538|             if( Downbeat != measure )
  1024. 539|                 printf( "\r%d", measure = Downbeat );
  1025. 540|         }
  1026. 541| 
  1027. 542|         printf( "\rDone" );
  1028. 543| 
  1029. 544|         D( printf("%d ticks overall\n", Numticks ); )
  1030. 545|         slowdown();
  1031. 546|         printf("\n");
  1032. 547| }
  1033.